package com.ap.core.context;

import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;
import org.springframework.beans.factory.FactoryBean;

import javax.xml.ws.BindingProvider;
import javax.xml.ws.Service;
import java.util.List;

/**
 * Useful Spring factory class that allows the configuration of interceptors and the endpoint on a CXF port.
 * @param <T> the type of the port to be configured.
 */
public class CXFPortFactory<T> implements FactoryBean<T> {

    private final T interceptedPort;

    /**
     * Set up the port factory to configure a specific port on a service by adding interceptors.
     * @param securedService The service providing the port to be configured.
     * @param portForInterception The class of the port that will be configured.
     * @param inInterceptorList The incoming interceptors to be <em>added</em> to the port.
     * @param outInterceptorList The outgoing interceptors to be <em>added</em> to the port.
     */
    public CXFPortFactory(Service securedService,
                          Class<T> portForInterception,
                          List<WSS4JInInterceptor> inInterceptorList,
                          List<WSS4JOutInterceptor> outInterceptorList) {
        interceptedPort = securedService.getPort(portForInterception);
        BindingProvider port = (BindingProvider) interceptedPort;
        setupInterceptors(port, inInterceptorList, outInterceptorList);
    }

    /**
     * Set up the port factory to configure a specific port on a service by adding interceptors and overriding the default
     * endpoint address.
     * @param securedService The service providing the port to be configured.
     * @param portForInterception The class of the port that will be configured.
     * @param inInterceptorList The incoming interceptors to be <em>added</em> to the port.
     * @param outInterceptorList The outgoing interceptors to be <em>added</em> to the port.
     * @param endpointAddress the endpoint address to be configured for the port.
     */
    public CXFPortFactory(Service securedService,
                          Class<T> portForInterception,
                          List<WSS4JInInterceptor> inInterceptorList,
                          List<WSS4JOutInterceptor> outInterceptorList,
                          String endpointAddress) {
        interceptedPort = securedService.getPort(portForInterception);
        BindingProvider port = (BindingProvider) interceptedPort;
        setupEndpointAddress(port, endpointAddress);
        setupInterceptors(port, inInterceptorList, outInterceptorList);
    }

    @Override
    public T getObject() throws Exception {
        return interceptedPort;
    }

    @Override
    public Class<?> getObjectType() {
        return interceptedPort.getClass();
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    private void setupInterceptors(BindingProvider port,
                                   List<WSS4JInInterceptor> inInterceptorList,
                                   List<WSS4JOutInterceptor> outInterceptorList) {
        Endpoint endpoint = ClientProxy.getClient(port).getEndpoint();
        endpoint.getInInterceptors().addAll(inInterceptorList);
        endpoint.getOutInterceptors().addAll(outInterceptorList);
    }

    private void setupEndpointAddress(BindingProvider port, String endpointAddress) {
         port.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointAddress);
    }

}
